using System;
using System.Collections.Generic;
using System.IO;

namespace UnityEngine.Recorder.Input
{
	public class RenderTextureSampler : BaseRenderTextureInput
	{
		private class HookedCamera
		{
			public Camera camera;

			public RenderTexture textureBackup;
		}

		private Shader superShader;

		private Shader accumulateShader;

		private Shader normalizeShader;

		private TextureFlipper m_VFlipper;

		private RenderTexture m_renderRT;

		private RenderTexture[] m_accumulateRTs = new RenderTexture[2];

		private int m_renderWidth;

		private int m_renderHeight;

		private Material m_superMaterial;

		private Material m_accumulateMaterial;

		private Material m_normalizeMaterial;

		private List<HookedCamera> m_hookedCameras;

		private Vector2[] m_samples;

		private RenderTextureSamplerSettings rtsSettings => (RenderTextureSamplerSettings)base.settings;

		private void GenerateSamplesMSAA(Vector2[] samples, ESuperSamplingCount sc)
		{
			switch (sc)
			{
			case ESuperSamplingCount.x1:
				samples[0] = new Vector2(0f, 0f);
				break;
			case ESuperSamplingCount.x2:
				samples[0] = new Vector2(4f, 4f);
				samples[1] = new Vector2(-4f, -4f);
				break;
			case ESuperSamplingCount.x4:
				samples[0] = new Vector2(-2f, -6f);
				samples[1] = new Vector2(6f, -2f);
				samples[2] = new Vector2(-6f, 2f);
				samples[3] = new Vector2(2f, 6f);
				break;
			case ESuperSamplingCount.x8:
				samples[0] = new Vector2(1f, -3f);
				samples[1] = new Vector2(-1f, 3f);
				samples[2] = new Vector2(5f, 1f);
				samples[3] = new Vector2(-3f, -5f);
				samples[4] = new Vector2(-5f, 5f);
				samples[5] = new Vector2(-7f, -1f);
				samples[6] = new Vector2(3f, 7f);
				samples[7] = new Vector2(7f, -7f);
				break;
			case ESuperSamplingCount.x16:
				samples[0] = new Vector2(1f, 1f);
				samples[1] = new Vector2(-1f, -3f);
				samples[2] = new Vector2(-3f, 2f);
				samples[3] = new Vector2(4f, -1f);
				samples[4] = new Vector2(-5f, -2f);
				samples[5] = new Vector2(2f, 5f);
				samples[6] = new Vector2(5f, 3f);
				samples[7] = new Vector2(3f, -5f);
				samples[8] = new Vector2(-2f, 6f);
				samples[9] = new Vector2(0f, -7f);
				samples[10] = new Vector2(-4f, -6f);
				samples[11] = new Vector2(-6f, 4f);
				samples[12] = new Vector2(-8f, 0f);
				samples[13] = new Vector2(7f, -4f);
				samples[14] = new Vector2(6f, 7f);
				samples[15] = new Vector2(-7f, -8f);
				break;
			default:
				UnityEngine.Debug.LogError("Not expected sample count: " + sc);
				return;
			}
			Vector2 b = new Vector2(0.5f, 0.5f);
			for (int i = 0; i < samples.Length; i++)
			{
				samples[i] = samples[i] * 0.0625f + b;
			}
		}

		public override void BeginRecording(RecordingSession session)
		{
			superShader = Shader.Find("Hidden/Volund/BS4SuperShader");
			accumulateShader = Shader.Find("Hidden/BeautyShot/Accumulate");
			normalizeShader = Shader.Find("Hidden/BeautyShot/Normalize");
			if (rtsSettings.m_FlipFinalOutput)
			{
				m_VFlipper = new TextureFlipper();
			}
			if (rtsSettings.m_OutputSize > rtsSettings.m_RenderSize)
			{
				throw new UnityException("Upscaling is not supported! Output dimension must be smaller or equal to render dimension.");
			}
			float realAR = AspectRatioHelper.GetRealAR(rtsSettings.m_AspectRatio);
			m_renderHeight = (int)rtsSettings.m_RenderSize;
			m_renderWidth = Mathf.Min(16384, Mathf.RoundToInt((float)m_renderHeight * realAR));
			base.outputHeight = (int)rtsSettings.m_OutputSize;
			base.outputWidth = Mathf.Min(16384, Mathf.RoundToInt((float)base.outputHeight * realAR));
			if (rtsSettings.m_ForceEvenSize)
			{
				base.outputWidth = ((base.outputWidth + 1) & -2);
				base.outputHeight = ((base.outputHeight + 1) & -2);
			}
			m_superMaterial = new Material(superShader);
			m_superMaterial.hideFlags = HideFlags.DontSave;
			m_accumulateMaterial = new Material(accumulateShader);
			m_accumulateMaterial.hideFlags = HideFlags.DontSave;
			m_normalizeMaterial = new Material(normalizeShader);
			m_normalizeMaterial.hideFlags = HideFlags.DontSave;
			m_renderRT = new RenderTexture(m_renderWidth, m_renderHeight, 24, RenderTextureFormat.DefaultHDR, RenderTextureReadWrite.Linear);
			m_renderRT.wrapMode = TextureWrapMode.Clamp;
			for (int i = 0; i < 2; i++)
			{
				m_accumulateRTs[i] = new RenderTexture(m_renderWidth, m_renderHeight, 0, RenderTextureFormat.DefaultHDR, RenderTextureReadWrite.Linear);
				m_accumulateRTs[i].wrapMode = TextureWrapMode.Clamp;
				m_accumulateRTs[i].Create();
			}
			RenderTexture renderTexture = new RenderTexture(base.outputWidth, base.outputHeight, 0, RenderTextureFormat.DefaultHDR, RenderTextureReadWrite.Linear);
			renderTexture.Create();
			base.outputRT = renderTexture;
			m_samples = new Vector2[(int)rtsSettings.m_SuperSampling];
			GenerateSamplesMSAA(m_samples, rtsSettings.m_SuperSampling);
			m_hookedCameras = new List<HookedCamera>();
		}

		public override void NewFrameStarting(RecordingSession session)
		{
			switch (rtsSettings.source)
			{
			case EImageSource.ActiveCameras:
			{
				bool flag2 = false;
				Camera[] array2 = Resources.FindObjectsOfTypeAll<Camera>();
				foreach (Camera cam in array2)
				{
					HookedCamera hookedCamera2 = m_hookedCameras.Find((HookedCamera x) => cam == x.camera);
					if (hookedCamera2 != null)
					{
						if (cam.targetDisplay != 0 || !cam.enabled)
						{
							UnityHelpers.Destroy(cam.targetTexture);
							cam.targetTexture = hookedCamera2.textureBackup;
							m_hookedCameras.Remove(hookedCamera2);
						}
					}
					else if (cam.enabled && cam.gameObject.activeInHierarchy && cam.targetDisplay == 0)
					{
						HookedCamera hookedCamera = new HookedCamera();
						hookedCamera.camera = cam;
						hookedCamera.textureBackup = cam.targetTexture;
						hookedCamera2 = hookedCamera;
						RenderTexture targetTexture = new RenderTexture((int)((float)m_renderWidth * cam.rect.width), (int)((float)m_renderHeight * cam.rect.height), 24, RenderTextureFormat.DefaultHDR, RenderTextureReadWrite.Linear);
						cam.targetTexture = targetTexture;
						m_hookedCameras.Add(hookedCamera2);
						flag2 = true;
					}
				}
				if (flag2)
				{
					m_hookedCameras.Sort((HookedCamera x, HookedCamera y) => (x.camera.depth < y.camera.depth) ? (-1) : ((x.camera.depth > y.camera.depth) ? 1 : 0));
				}
				break;
			}
			case EImageSource.MainCamera:
			{
				Camera main = Camera.main;
				if (m_hookedCameras.Count > 0)
				{
					if (!(m_hookedCameras[0].camera != main))
					{
						break;
					}
					m_hookedCameras[0].camera.targetTexture = m_hookedCameras[0].textureBackup;
					m_hookedCameras.Clear();
				}
				if (main.enabled)
				{
					HookedCamera hookedCamera = new HookedCamera();
					hookedCamera.camera = main;
					hookedCamera.textureBackup = main.targetTexture;
					HookedCamera item2 = hookedCamera;
					main.targetTexture = m_renderRT;
					m_hookedCameras.Add(item2);
				}
				break;
			}
			case EImageSource.TaggedCamera:
			{
				string cameraTag = (base.settings as RenderTextureSamplerSettings).m_CameraTag;
				GameObject[] array;
				try
				{
					array = GameObject.FindGameObjectsWithTag(cameraTag);
				}
				catch (UnityException)
				{
					UnityEngine.Debug.LogWarning("No camera has the requested target tag:" + cameraTag);
					array = new GameObject[0];
				}
				for (int num = m_hookedCameras.Count - 1; num >= 0; num--)
				{
					if (m_hookedCameras[num].camera.gameObject.tag != cameraTag)
					{
						m_hookedCameras[num].camera.targetTexture = m_hookedCameras[num].textureBackup;
						m_hookedCameras.RemoveAt(num);
					}
				}
				for (int i = 0; i < array.Length; i++)
				{
					bool flag = false;
					Camera component = array[i].transform.GetComponent<Camera>();
					if (!(component != null) || !component.enabled)
					{
						continue;
					}
					for (int j = 0; j < m_hookedCameras.Count; j++)
					{
						if (m_hookedCameras[j].camera == array[i].transform.GetComponent<Camera>())
						{
							flag = true;
							break;
						}
					}
					if (!flag)
					{
						HookedCamera hookedCamera = new HookedCamera();
						hookedCamera.camera = component;
						hookedCamera.textureBackup = component.targetTexture;
						HookedCamera item = hookedCamera;
						component.targetTexture = m_renderRT;
						m_hookedCameras.Add(item);
					}
				}
				break;
			}
			default:
				throw new ArgumentOutOfRangeException();
			}
		}

		protected override void Dispose(bool disposing)
		{
			if (disposing)
			{
				if (m_hookedCameras != null)
				{
					foreach (HookedCamera hookedCamera in m_hookedCameras)
					{
						if (hookedCamera != null)
						{
							if (hookedCamera.camera.rect.width == 1f && hookedCamera.camera.rect.height == 1f)
							{
								UnityHelpers.Destroy(hookedCamera.camera.targetTexture);
							}
							hookedCamera.camera.targetTexture = hookedCamera.textureBackup;
						}
					}
					m_hookedCameras.Clear();
				}
				UnityHelpers.Destroy(m_renderRT);
				RenderTexture[] accumulateRTs = m_accumulateRTs;
				foreach (RenderTexture obj in accumulateRTs)
				{
					UnityHelpers.Destroy(obj);
				}
				UnityHelpers.Destroy(m_superMaterial);
				UnityHelpers.Destroy(m_accumulateMaterial);
				UnityHelpers.Destroy(m_normalizeMaterial);
				if (m_VFlipper != null)
				{
					m_VFlipper.Dispose();
				}
			}
			base.Dispose(disposing);
		}

		public override void NewFrameReady(RecordingSession session)
		{
			PerformSubSampling();
			if (rtsSettings.m_RenderSize == rtsSettings.m_OutputSize)
			{
				m_normalizeMaterial.SetFloat("_NormalizationFactor", 1f / (float)rtsSettings.m_SuperSampling);
				m_normalizeMaterial.SetInt("_ApplyGammaCorrection", (QualitySettings.activeColorSpace == ColorSpace.Linear && rtsSettings.m_ColorSpace == ColorSpace.Gamma) ? 1 : 0);
				Graphics.Blit(m_renderRT, base.outputRT, m_normalizeMaterial);
			}
			else
			{
				m_superMaterial.SetVector("_Target_TexelSize", new Vector4(1f / (float)base.outputWidth, 1f / (float)base.outputHeight, base.outputWidth, base.outputHeight));
				m_superMaterial.SetFloat("_KernelCosPower", rtsSettings.m_SuperKernelPower);
				m_superMaterial.SetFloat("_KernelScale", rtsSettings.m_SuperKernelScale);
				m_superMaterial.SetFloat("_NormalizationFactor", 1f / (float)rtsSettings.m_SuperSampling);
				m_superMaterial.SetInt("_ApplyGammaCorrection", (QualitySettings.activeColorSpace == ColorSpace.Linear && rtsSettings.m_ColorSpace == ColorSpace.Gamma) ? 1 : 0);
				Graphics.Blit(m_renderRT, base.outputRT, m_superMaterial);
			}
			if (rtsSettings.m_FlipFinalOutput)
			{
				m_VFlipper.Flip(base.outputRT);
			}
		}

		private void ShiftProjectionMatrix(Camera camera, Vector2 sample)
		{
			camera.ResetProjectionMatrix();
			Matrix4x4 projectionMatrix = camera.projectionMatrix;
			float num = sample.x / (float)m_renderWidth;
			float num2 = sample.y / (float)m_renderHeight;
			projectionMatrix.m02 += num;
			projectionMatrix.m12 += num2;
			camera.projectionMatrix = projectionMatrix;
		}

		private bool CameraUsingPartialViewport(Camera cam)
		{
			return cam.rect.width != 1f || cam.rect.height != 1f || cam.rect.x != 0f || cam.rect.y != 0f;
		}

		private void PerformSubSampling()
		{
			RenderTexture renderTexture = null;
			m_renderRT.wrapMode = TextureWrapMode.Clamp;
			m_renderRT.filterMode = FilterMode.Point;
			int num = 0;
			Graphics.SetRenderTarget(m_accumulateRTs[0]);
			GL.Clear(clearDepth: false, clearColor: true, Color.black);
			foreach (HookedCamera hookedCamera in m_hookedCameras)
			{
				Camera camera = hookedCamera.camera;
				int i = 0;
				for (int superSampling = (int)rtsSettings.m_SuperSampling; i < superSampling; i++)
				{
					Matrix4x4 projectionMatrix = camera.projectionMatrix;
					Rect rect = camera.rect;
					camera.rect = new Rect(0f, 0f, 1f, 1f);
					ShiftProjectionMatrix(camera, m_samples[i] - new Vector2(0.5f, 0.5f));
					camera.Render();
					camera.projectionMatrix = projectionMatrix;
					camera.rect = rect;
					renderTexture = m_accumulateRTs[(num + 1) % 2];
					RenderTexture value = m_accumulateRTs[num % 2];
					m_accumulateMaterial.SetTexture("_PreviousTexture", value);
					if (CameraUsingPartialViewport(camera))
					{
						m_accumulateMaterial.SetFloat("_OfsX", camera.rect.x);
						m_accumulateMaterial.SetFloat("_OfsY", camera.rect.y);
						m_accumulateMaterial.SetFloat("_Width", camera.rect.width);
						m_accumulateMaterial.SetFloat("_Height", camera.rect.height);
						m_accumulateMaterial.SetFloat("_Scale", (float)camera.targetTexture.width / (float)m_renderRT.width);
					}
					else
					{
						m_accumulateMaterial.SetFloat("_OfsX", 0f);
						m_accumulateMaterial.SetFloat("_OfsY", 0f);
						m_accumulateMaterial.SetFloat("_Width", 1f);
						m_accumulateMaterial.SetFloat("_Height", 1f);
						m_accumulateMaterial.SetFloat("_Scale", 1f);
					}
					m_accumulateMaterial.SetInt("_Pass", i);
					Graphics.Blit(camera.targetTexture, renderTexture, m_accumulateMaterial);
					num++;
				}
			}
			Graphics.Blit(renderTexture, m_renderRT);
		}

		private void SaveRT(RenderTexture input)
		{
			if (!(input == null))
			{
				int width = input.width;
				int height = input.height;
				Texture2D texture2D = new Texture2D(width, height, TextureFormat.RGBA32, mipChain: false);
				RenderTexture active = RenderTexture.active;
				RenderTexture.active = input;
				texture2D.ReadPixels(new Rect(0f, 0f, width, height), 0, 0);
				texture2D.Apply();
				RenderTexture.active = active;
				byte[] bytes = texture2D.EncodeToPNG();
				UnityHelpers.Destroy(texture2D);
				File.WriteAllBytes("Recorder/DebugDump.png", bytes);
			}
		}
	}
}
